package p4;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.Random;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;

public class Saolei extends JPanel implements ActionListener, MouseListener {

    private static final int WIDTH = 15;//格子的列数
    private static final int HEIGHT = 15;//格子的行数
    private int[][] initial = new int[WIDTH][HEIGHT];//格子的显示状态
    private int[][] shape = new int[WIDTH][HEIGHT];//格子的存储对象
    private Graphics g;
    private String str;
    private int x0, y0;
    private JFrame jf = new JFrame("扫雷");//游戏窗体
    private JFrame jfr = new JFrame("选择难度");//选择难度窗体
    private JFrame jfra = new JFrame("游戏结果");//选择失败窗体
    private JFrame jfram = new JFrame("游戏结果");//选择胜利窗体
    private int firstbegin = 0, firstover = 0, firstvictory = 0,victory;

    public static void main(String[] agrs) {
        Saolei game = new Saolei();
        game._begin();
    }

    public void _begin() {
        jf.setSize(1000, 1000);
        jf.setLocationRelativeTo(null);
        jf.setDefaultCloseOperation(3);
        jf.setLayout(new BorderLayout());
        JPanel jp = new JPanel();
        jf.add(jp, BorderLayout.NORTH);
        String[] str = { "开始游戏", "标记地雷","取消标记", "退出标记", "重新开始", "退出游戏" };
        for (int i = 0; i < str.length; i++) {
            JButton jb = new JButton(str[i]);
            jp.add(jb);
            jb.addActionListener(this);
        }
        jf.add(this, BorderLayout.CENTER);
        this.addMouseListener(this);
        jf.setVisible(true);
        g = this.getGraphics();
    }
//    在开始游戏时要进行游戏数据的初始化并在后续的游戏操作中不断改变数据。首先设置所有的格子为闭状态。
    public void setIn() {
        for (int i = 0; i < WIDTH; i++) {
            for (int j = 0; j < HEIGHT; j++) {
                initial[i][j] = 0;
            }
        }
    }
//当第一次开始游戏或多次进行游戏时，不仅格子开闭状态需要重置，雷的坐标也需要重新设置，要达到这一效果就需要将之前设置的所有的雷清除，然后再重新设置雷。
    public void initiallei() {
        for (int i = 0; i < WIDTH; i++) {
            for (int j = 0; j < HEIGHT; j++) {
                shape[i][j] = 0;
            }
        }
    }
//在打开方法中设置了打开雷的格子弹出游戏失败的窗体，接下来要设置游戏胜利的窗体。首先游戏胜利的条件为除了雷以外的格子全部打开，
// 换句话说，剩下的格子应该全是雷。因此写一个方法来统计未打开的格子数，在游戏开始时，所有的格子都是未打开的，进行游戏时，
// 非雷的格子不断被打开，因此，未打开的格子数不断减少，当未打开的格子数减少到与雷数相同时，
// 则弹出游戏胜利的窗体（在游戏过程中，当有雷的格子被打开时，游戏已经失败）。在此处引入标记雷的方法，
// 这时之前统计未打开的格子数的方法需要修改一下，改为统计标记的格子和未打开的格子（只有当标记的格子是雷，才能取得游戏胜利，
// 因为当标记错误，在统计未打开和被标记的格子数的过程中一定会打开有雷的格子）。增加了标记雷的功能，在相应的open方法中也做了相应的修改，
// 具体修改参见上一步的代码（在判断条件中增加了initial[x][y] == 2的标记情况）。
    public int countIn() {

        int count = 0;
        for (int i = 0; i < WIDTH; i++) {
            for (int j = 0; j < HEIGHT; j++) {
                if (initial[i][j] == 2||initial[i][j]==0) {
                    count++;
                }
            }
        }
        return count;
    }
//接着设置雷的数量，根据不同的难度来确定设置的雷数。设置雷需要设置雷的坐标，因此设置两个随机变量，分别代表x，y坐标，
// 要求每个雷设置的坐标不重复，因此先获取一个随机坐标，然后判断该坐标是否已经存在雷，若已存在雷，则重新获取坐标，
// 这一步使用do，while循环来解决；若不存在雷，则退出循环，并把该坐标设置为有雷状态。
    public void setLei(int count) {
        int x, y;
        Random rx = new Random();
        Random ry = new Random();
        for (int i = 0; i < count; i++) {
            do {
                x = rx.nextInt(WIDTH);
                y = ry.nextInt(HEIGHT);
            } while (shape[x][y] == 9);
            shape[x][y] = 9;
        }
    }
//当雷设置完后，则需要设置其他格子的状态，按照游戏规则，每个非雷的格子上的数字表示以它为中心的九宫格所有的雷数，
// 因此，此处有两种方法计算这个数字。一种为在雷的周围8个格子加上一，将每个雷周围的格子都进行此操作即可；
// 另一种为计算每个格子周围有几个雷（此种方法计算次数相对较多，此处采用这种方法示例）
    public void setothers(int x, int y) {
        int current = 0;
        if (shape[x][y] != 9) {
            for (int i = 0; i < 3; i++) {
                for (int j = 0; j < 3; j++) {
                    if (x - 1 + i >= 0 && x - 1 + i <= 14 && y - 1 + j >= 0 && y - 1 + j <= 14) {
                        if (shape[x - 1 + i][y - 1 + j] == 9) {
                            current++;
                        }
                    }
                }
            }
            shape[x][y] = current;
        }
    }
//做完这些，游戏正式开始。此时要求在鼠标释放位置上的格子打开，且如果该格子为空白就自动判断四周的格子，
// 四周的格子中，空白的格子重复以上操作，为数字的格子则打开格子且不再向四周扩展，为雷的格子则不进行任何操作；
// 若在鼠标释放位置上的格子为数字则打开格子且不再向四周扩展；若该格子为雷则弹出游戏失败。
// 此处的打开操作运用了递归算法。以下代码中的initial[x][y] == 2为完善游戏功能所增加的数值，
// 它的作用是标记雷，与上面重写的绘制方法中的红旗图案相关联。
    public void open(int x, int y) {
        if ((initial[x][y] == 0 && shape[x][y] == 0) || (initial[x][y] == 2 && shape[x][y] == 0)) {
            initial[x][y] = 1;
            for (int i = 0; i < 3; i += 2) {
                if (x - 1 + i >= 0 && x - 1 + i <= 14) {
                    if (initial[x - 1 + i][y] == 0 && shape[x - 1 + i][y] != 9) {
                        open(x - 1 + i, y);
                    } else if (initial[x - 1 + i][y] == 2 && shape[x - 1 + i][y] != 9) {
                        open(x - 1 + i, y);
                    }
                }
            }
            for (int i = 0; i < 3; i += 2) {
                if (y - 1 + i >= 0 && y - 1 + i <= 14) {
                    if (initial[x][y - 1 + i] == 0 && shape[x][y - 1 + i] != 9) {
                        open(x, y - 1 + i);
                    } else if (initial[x][y - 1 + i] == 2 && shape[x][y - 1 + i] != 9) {
                        open(x, y - 1 + i);
                    }
                }
            }
        } else if ((initial[x][y] == 0 && shape[x][y] > 0 && shape[x][y] < 9)
                || (initial[x][y] == 2 && shape[x][y] > 0 && shape[x][y] < 9)) {
            initial[x][y] = 1;
        } else if ((initial[x][y] == 0 && shape[x][y] == 9) || (initial[x][y] == 2 && shape[x][y] == 9)) {
            initial[x][y] = 1;
            if (firstover == 0) {
                jfra.setLayout(new FlowLayout());
                ImageIcon image = new ImageIcon("image/over.png");
                JLabel jl = new JLabel(image);
                jl.setPreferredSize(new Dimension(image.getIconWidth() - 100, image.getIconHeight() - 100));
                jfra.setSize(new Dimension(image.getIconWidth(), image.getIconHeight()));
                jfra.setLocationRelativeTo(null);
                jfra.add(jl);
                String[] str = { "退出游戏", "重新开始" };
                for (int i = 0; i < str.length; i++) {
                    JButton jb = new JButton(str[i]);
                    jfra.add(jb);
                    jb.addActionListener(this);
                }
                jfra.setVisible(true);
                firstover++;
                victory--;
            }else {
                jfra.setVisible(true);
            }
        }
    }
//以上是统计方法，接下来是判断游戏胜利，并且弹出游戏胜利的窗体。其中的victory在选择难度后进行设置，在数值上等于雷数。
    public void setVictory() {
        int count;
        count = countIn();
        if (count == victory) {
            if (firstvictory == 0) {
                jfram.setLayout(new FlowLayout());
                ImageIcon image = new ImageIcon("image/victory.jpg");
                JLabel jl = new JLabel(image);
                jl.setPreferredSize(new Dimension(image.getIconWidth() - 100, image.getIconHeight() - 100));
                jfram.setSize(new Dimension(image.getIconWidth(), image.getIconHeight()));
                jfram.setLocationRelativeTo(null);
                jfram.add(jl);
                String[] str = { "退出游戏", "再玩一次" };
                for (int i = 0; i < str.length; i++) {
                    JButton jb = new JButton(str[i]);
                    jfram.add(jb);
                    jb.addActionListener(this);
                }
                jfram.setVisible(true);
                firstvictory++;
            } else {
                jfram.setVisible(true);
            }
        }
    }
//进行一次打开格子的操作后，都要对游戏界面进行绘制，具体如下：
    public void _paint() {
        for (int i = 0; i < WIDTH; i++) {
            for (int j = 0; j < HEIGHT; j++) {
                if (initial[i][j] == 1) {
                    String string = "image/" + shape[i][j] + ".png";
                    ImageIcon image = new ImageIcon(string);
                    Image im = image.getImage();
                    g.drawImage(im, 60 * i, 60 * j, 60, 60, null);
                } else if (initial[i][j] == 0) {
                    String string = "image/" + "10" + ".png";
                    ImageIcon image = new ImageIcon(string);
                    Image im = image.getImage();
                    g.drawImage(im, 60 * i, 60 * j, 60, 60, null);
                } else {
                    ImageIcon image = new ImageIcon("image/红旗.png");
                    Image im = image.getImage();
                    g.drawImage(im, 60 * i, 60 * j, 60, 60, null);
                }
            }
        }
    }

//当基础数据设置完毕后便开始游戏界面的设置，首先选好相应的图片与0~9的格子相匹配，还有一张格子未打卡开的图片。
// 当点击开始游戏按钮时，将图片绘制的面板上，此时运用到之前所设置的firstbegin变量，
// 第一次点击开始游戏后，firstbegin++，于是继承JPanel且重写的paint方法开始能够发挥作用。
    public void paint(Graphics g) {
        super.paint(g);
        if (firstbegin != 0) {
            for (int i = 0; i < WIDTH; i++) {
                for (int j = 0; j < HEIGHT; j++) {
                    if (initial[i][j] == 1) {
                        String string = "image/" + shape[i][j] + ".png";
                        ImageIcon image = new ImageIcon(string);
                        Image im = image.getImage();
                        g.drawImage(im, 60 * i, 60 * j, 60, 60, null);
                    } else if (initial[i][j] == 0) {
                        String string = "image/" + "10" + ".png";
                        ImageIcon image = new ImageIcon(string);
                        Image im = image.getImage();
                        g.drawImage(im, 60 * i, 60 * j, 60, 60, null);
                    } else {
                        ImageIcon image = new ImageIcon("image/红旗.png");
                        Image im = image.getImage();
                        g.drawImage(im, 60 * i, 60 * j, 60, 60, null);
                    }
                }
            }
        }

    }

    @Override
    public void mouseClicked(MouseEvent e) {
        // TODO Auto-generated method stub

    }

    @Override
    public void mousePressed(MouseEvent e) {
        // TODO Auto-generated method stub

    }
//进行游戏操作的步骤，即在鼠标释放的格子进行打开，并将更改后的格子重新绘制，
// 然后再判断是否游戏胜利，游戏失败的判断在open方法中（游戏失败总是在游戏胜利之前，因此先执行）。
    @Override
    public void mouseReleased(MouseEvent e) {

        if (firstbegin != 0 && str.equals("标记地雷") == false &&str.equals("取消标记") == false|| (firstbegin != 0 &&str.equals("退出标记"))) {
            x0 = (int) e.getX() / 60;
            y0 = (int) e.getY() / 60;
            open(x0, y0);
            _paint();
            setVictory();
        }


        if (firstbegin != 0 &&str.equals("标记地雷")) {
            x0 = (int) e.getX() / 60;
            y0 = (int) e.getY() / 60;
            if(initial[x0][y0]!=1) {
                initial[x0][y0] = 2;
                _paint();
            }
        }
        if(firstbegin != 0&&str.equals("取消标记")) {
            x0 = (int) e.getX() / 60;
            y0 = (int) e.getY() / 60;
            initial[x0][y0] = 0;
            _paint();
        }
    }

    @Override
    public void mouseEntered(MouseEvent e) {
        // TODO Auto-generated method stub

    }

    @Override
    public void mouseExited(MouseEvent e) {
        // TODO Auto-generated method stub

    }
//此外点击开始游戏还需要弹出难度选择的窗体，并根据选择的难度来进行以下一系列的设置。
//最后便是对游戏失败和游戏胜利的窗体进行操作，游戏失败后有两个选择，重新开始或退出游戏；
// 游戏胜利后也有两个选择，再玩一次或重新开始。点击这些按钮后进行的监听方法如下：
    @Override
    public void actionPerformed(ActionEvent e) {
        str = e.getActionCommand();
        if (str.equals("退出游戏")) {
            jf.dispose();
            jfr.dispose();
            jfra.dispose();
            jfram.dispose();
        } else if (str.equals("开始游戏") && firstbegin == 0) {
            firstbegin++;
            jfr.setSize(300, 80);
            jfr.setLocationRelativeTo(null);
            jfr.setLayout(new FlowLayout());
            String[] str1 = { "简单", "中等", "困难" };
            for (int i = 0; i < str1.length; i++) {
                JButton jb = new JButton(str1[i]);
                jfr.add(jb);
                jb.addActionListener(this);
            }
            jfr.setVisible(true);
        } else if (str.equals("再玩一次")) {
            jfr.setVisible(true);
            jfram.dispose();
        } else if (str.equals("重新开始")) {
            jfr.setVisible(true);
            jfra.dispose();
        }
        if (str.equals("简单")) {
            victory=15;
            setIn();
            initiallei();
            setLei(15);
            for (int i = 0; i < WIDTH; i++) {
                for (int j = 0; j < HEIGHT; j++) {
                    setothers(i, j);
                }
            }
            jfr.setVisible(false);
            _paint();
        }
        if (str.equals("中等")) {
            victory=30;
            setIn();
            initiallei();
            setLei(30);
            for (int i = 0; i < WIDTH; i++) {
                for (int j = 0; j < HEIGHT; j++) {
                    setothers(i, j);
                }
            }
            jfr.setVisible(false);
            _paint();
        }
        if (str.equals("困难")) {
            victory=45;
            setIn();
            initiallei();
            setLei(45);
            for (int i = 0; i < WIDTH; i++) {
                for (int j = 0; j < HEIGHT; j++) {
                    setothers(i, j);
                }
            }
            jfr.setVisible(false);
            _paint();
        }
    }

}
